// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/input/gesture_event_queue.h"

#include <stddef.h>

#include <memory>
#include <utility>
#include <vector>

#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "content/browser/renderer_host/input/touchpad_tap_suppression_controller.h"
#include "content/common/input/input_event_ack_state.h"
#include "content/common/input/synthetic_web_input_event_builders.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/WebInputEvent.h"

using base::TimeDelta;
using blink::WebGestureDevice;
using blink::WebGestureEvent;
using blink::WebInputEvent;

namespace content {

class GestureEventQueueTest : public testing::Test,
                              public GestureEventQueueClient,
                              public TouchpadTapSuppressionControllerClient {
public:
    GestureEventQueueTest()
        : acked_gesture_event_count_(0)
        , sent_gesture_event_count_(0)
    {
    }

    ~GestureEventQueueTest() override { }

    // testing::Test
    void SetUp() override
    {
        queue_.reset(new GestureEventQueue(this, this, DefaultConfig()));
    }

    void TearDown() override
    {
        // Process all pending tasks to avoid leaks.
        RunUntilIdle();
        queue_.reset();
    }

    // GestureEventQueueClient
    void SendGestureEventImmediately(
        const GestureEventWithLatencyInfo& event) override
    {
        ++sent_gesture_event_count_;
        if (sync_ack_result_) {
            std::unique_ptr<InputEventAckState> ack_result = std::move(sync_ack_result_);
            SendInputEventACK(event.event.type(), *ack_result);
        }
    }

    void OnGestureEventAck(const GestureEventWithLatencyInfo& event,
        InputEventAckState ack_result) override
    {
        ++acked_gesture_event_count_;
        last_acked_event_ = event.event;
        if (sync_followup_event_) {
            auto sync_followup_event = std::move(sync_followup_event_);
            SimulateGestureEvent(*sync_followup_event);
        }
    }

    // TouchpadTapSuppressionControllerClient
    void SendMouseEventImmediately(
        const MouseEventWithLatencyInfo& event) override { }

protected:
    static GestureEventQueue::Config DefaultConfig()
    {
        return GestureEventQueue::Config();
    }

    void SetUpForDebounce(int interval_ms)
    {
        queue()->set_debounce_interval_time_ms_for_testing(interval_ms);
    }

    void SimulateGestureEvent(const WebGestureEvent& gesture)
    {
        queue()->QueueEvent(GestureEventWithLatencyInfo(gesture));
    }

    void SimulateGestureEvent(WebInputEvent::Type type,
        WebGestureDevice sourceDevice)
    {
        SimulateGestureEvent(
            SyntheticWebGestureEventBuilder::Build(type, sourceDevice));
    }

    void SimulateGestureScrollUpdateEvent(float dX, float dY, int modifiers)
    {
        SimulateGestureEvent(SyntheticWebGestureEventBuilder::BuildScrollUpdate(
            dX, dY, modifiers, blink::WebGestureDeviceTouchscreen));
    }

    void SimulateGesturePinchUpdateEvent(float scale,
        float anchorX,
        float anchorY,
        int modifiers)
    {
        SimulateGestureEvent(SyntheticWebGestureEventBuilder::BuildPinchUpdate(
            scale,
            anchorX,
            anchorY,
            modifiers,
            blink::WebGestureDeviceTouchscreen));
    }

    void SimulateGestureFlingStartEvent(float velocityX,
        float velocityY,
        WebGestureDevice sourceDevice)
    {
        SimulateGestureEvent(
            SyntheticWebGestureEventBuilder::BuildFling(velocityX,
                velocityY,
                sourceDevice));
    }

    void SendInputEventACK(WebInputEvent::Type type,
        InputEventAckState ack)
    {
        queue()->ProcessGestureAck(ack, type, ui::LatencyInfo());
    }

    void RunUntilIdle() { base::RunLoop().RunUntilIdle(); }

    size_t GetAndResetSentGestureEventCount()
    {
        size_t count = sent_gesture_event_count_;
        sent_gesture_event_count_ = 0;
        return count;
    }

    size_t GetAndResetAckedGestureEventCount()
    {
        size_t count = acked_gesture_event_count_;
        acked_gesture_event_count_ = 0;
        return count;
    }

    const WebGestureEvent& last_acked_event() const
    {
        return last_acked_event_;
    }

    void set_synchronous_ack(InputEventAckState ack_result)
    {
        sync_ack_result_.reset(new InputEventAckState(ack_result));
    }

    void set_sync_followup_event(WebInputEvent::Type type,
        WebGestureDevice sourceDevice)
    {
        sync_followup_event_.reset(new WebGestureEvent(
            SyntheticWebGestureEventBuilder::Build(type, sourceDevice)));
    }

    unsigned GestureEventQueueSize()
    {
        return queue()->coalesced_gesture_events_.size();
    }

    WebGestureEvent GestureEventSecondFromLastQueueEvent()
    {
        return queue()->coalesced_gesture_events_.at(
                                                     GestureEventQueueSize() - 2)
            .event;
    }

    WebGestureEvent GestureEventLastQueueEvent()
    {
        return queue()->coalesced_gesture_events_.back().event;
    }

    unsigned GestureEventDebouncingQueueSize()
    {
        return queue()->debouncing_deferral_queue_.size();
    }

    WebGestureEvent GestureEventQueueEventAt(int i)
    {
        return queue()->coalesced_gesture_events_.at(i).event;
    }

    bool ScrollingInProgress()
    {
        return queue()->scrolling_in_progress_;
    }

    bool FlingInProgress() { return queue()->fling_in_progress_; }

    bool WillIgnoreNextACK()
    {
        return queue()->ignore_next_ack_;
    }

    GestureEventQueue* queue() const
    {
        return queue_.get();
    }

private:
    std::unique_ptr<GestureEventQueue> queue_;
    size_t acked_gesture_event_count_;
    size_t sent_gesture_event_count_;
    WebGestureEvent last_acked_event_;
    std::unique_ptr<InputEventAckState> sync_ack_result_;
    std::unique_ptr<WebGestureEvent> sync_followup_event_;
    base::MessageLoopForUI message_loop_;
};

#if GTEST_HAS_PARAM_TEST
// This is for tests that are to be run for all source devices.
class GestureEventQueueWithSourceTest
    : public GestureEventQueueTest,
      public testing::WithParamInterface<WebGestureDevice> {
};
#endif // GTEST_HAS_PARAM_TEST

TEST_F(GestureEventQueueTest, CoalescesScrollGestureEvents)
{
    // Test coalescing of only GestureScrollUpdate events.
    // Simulate gesture events.

    // Sent.
    SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());

    // Enqueued.
    SimulateGestureScrollUpdateEvent(8, -5, 0);

    // Make sure that the queue contains what we think it should.
    WebGestureEvent merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(2U, GestureEventQueueSize());
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);

    // Coalesced.
    SimulateGestureScrollUpdateEvent(8, -6, 0);

    // Check that coalescing updated the correct values.
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(0, merged_event.modifiers());
    EXPECT_EQ(16, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-11, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);

    // Enqueued.
    SimulateGestureScrollUpdateEvent(8, -7, 1);

    // Check that we didn't wrongly coalesce.
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);

    // Different.
    SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
        blink::WebGestureDeviceTouchscreen);

    // Check that only the first event was sent.
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());

    // Check that the ACK sends the second message.
    SendInputEventACK(WebInputEvent::GestureScrollBegin,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    RunUntilIdle();
    EXPECT_EQ(1U, GetAndResetAckedGestureEventCount());
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());

    // Ack for queued coalesced event.
    SendInputEventACK(WebInputEvent::GestureScrollUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    RunUntilIdle();
    EXPECT_EQ(1U, GetAndResetAckedGestureEventCount());
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());

    // Ack for queued uncoalesced event.
    SendInputEventACK(WebInputEvent::GestureScrollUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    RunUntilIdle();
    EXPECT_EQ(1U, GetAndResetAckedGestureEventCount());
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());

    // After the final ack, the queue should be empty.
    SendInputEventACK(WebInputEvent::GestureScrollEnd,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    RunUntilIdle();
    EXPECT_EQ(1U, GetAndResetAckedGestureEventCount());
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
}

TEST_F(GestureEventQueueTest,
    DoesNotCoalesceScrollGestureEventsFromDifferentDevices)
{
    // Test that GestureScrollUpdate events from Touchscreen and Touchpad do not
    // coalesce.

    // Sent.
    SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());

    // Enqueued.
    SimulateGestureScrollUpdateEvent(8, -5, 0);

    // Make sure that the queue contains what we think it should.
    EXPECT_EQ(2U, GestureEventQueueSize());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen,
        GestureEventLastQueueEvent().sourceDevice);

    // Coalesced.
    SimulateGestureScrollUpdateEvent(8, -6, 0);
    EXPECT_EQ(2U, GestureEventQueueSize());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen,
        GestureEventLastQueueEvent().sourceDevice);

    // Enqueued.
    SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
        blink::WebGestureDeviceTouchpad);
    EXPECT_EQ(3U, GestureEventQueueSize());
    EXPECT_EQ(blink::WebGestureDeviceTouchpad,
        GestureEventLastQueueEvent().sourceDevice);

    // Coalesced.
    SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
        blink::WebGestureDeviceTouchpad);
    EXPECT_EQ(3U, GestureEventQueueSize());
    EXPECT_EQ(blink::WebGestureDeviceTouchpad,
        GestureEventLastQueueEvent().sourceDevice);

    // Enqueued.
    SimulateGestureScrollUpdateEvent(8, -7, 0);
    EXPECT_EQ(4U, GestureEventQueueSize());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen,
        GestureEventLastQueueEvent().sourceDevice);
}

TEST_F(GestureEventQueueTest, CoalescesScrollAndPinchEvents)
{
    // Test coalescing of only GestureScrollUpdate events.
    // Simulate gesture events.

    // Sent.
    SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
        blink::WebGestureDeviceTouchscreen);

    // Sent.
    SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
        blink::WebGestureDeviceTouchscreen);

    // Enqueued.
    SimulateGestureScrollUpdateEvent(8, -4, 1);

    // Make sure that the queue contains what we think it should.
    WebGestureEvent merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(3U, GestureEventQueueSize());
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());

    // Coalesced without changing event order. Note anchor at (60, 60). Anchoring
    // from a point that is not the origin should still give us the right scroll.
    SimulateGesturePinchUpdateEvent(1.5, 60, 60, 1);
    EXPECT_EQ(4U, GestureEventQueueSize());
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type());
    EXPECT_EQ(1.5, merged_event.data.pinchUpdate.scale);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
    merged_event = GestureEventSecondFromLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(8, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-4, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);

    // Enqueued.
    SimulateGestureScrollUpdateEvent(6, -3, 1);

    // Check whether coalesced correctly.
    EXPECT_EQ(4U, GestureEventQueueSize());
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type());
    EXPECT_EQ(1.5, merged_event.data.pinchUpdate.scale);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
    merged_event = GestureEventSecondFromLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(12, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-6, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);

    // Enqueued.
    SimulateGesturePinchUpdateEvent(2, 60, 60, 1);

    // Check whether coalesced correctly.
    EXPECT_EQ(4U, GestureEventQueueSize());
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type());
    EXPECT_EQ(3, merged_event.data.pinchUpdate.scale);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
    merged_event = GestureEventSecondFromLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(12, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-6, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);

    // Enqueued.
    SimulateGesturePinchUpdateEvent(2, 60, 60, 1);

    // Check whether coalesced correctly.
    EXPECT_EQ(4U, GestureEventQueueSize());
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type());
    EXPECT_EQ(6, merged_event.data.pinchUpdate.scale);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
    merged_event = GestureEventSecondFromLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(12, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-6, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);

    // Check that only the first event was sent.
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());

    // Check that the ACK sends the second message.
    SendInputEventACK(WebInputEvent::GestureScrollBegin,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    RunUntilIdle();
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());

    // Enqueued.
    SimulateGestureScrollUpdateEvent(6, -6, 1);

    // Check whether coalesced correctly.
    EXPECT_EQ(3U, GestureEventQueueSize());
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type());
    EXPECT_EQ(6, merged_event.data.pinchUpdate.scale);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
    merged_event = GestureEventSecondFromLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(13, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-7, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);

    // At this point ACKs shouldn't be getting ignored.
    EXPECT_FALSE(WillIgnoreNextACK());

    // Check that the ACK sends both scroll and pinch updates.
    SendInputEventACK(WebInputEvent::GesturePinchBegin,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    RunUntilIdle();
    EXPECT_EQ(2U, GetAndResetSentGestureEventCount());

    // The next ACK should be getting ignored.
    EXPECT_TRUE(WillIgnoreNextACK());

    // Enqueued.
    SimulateGestureScrollUpdateEvent(1, -1, 1);

    // Check whether coalesced correctly.
    EXPECT_EQ(3U, GestureEventQueueSize());
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(1, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-1, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
    merged_event = GestureEventSecondFromLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type());
    EXPECT_EQ(6, merged_event.data.pinchUpdate.scale);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);

    // Enqueued.
    SimulateGestureScrollUpdateEvent(2, -2, 1);

    // Coalescing scrolls should still work.
    EXPECT_EQ(3U, GestureEventQueueSize());
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(3, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-3, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
    merged_event = GestureEventSecondFromLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type());
    EXPECT_EQ(6, merged_event.data.pinchUpdate.scale);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);

    // Enqueued.
    SimulateGesturePinchUpdateEvent(0.5, 60, 60, 1);

    // Check whether coalesced correctly.
    EXPECT_EQ(4U, GestureEventQueueSize());
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type());
    EXPECT_EQ(0.5, merged_event.data.pinchUpdate.scale);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
    merged_event = GestureEventSecondFromLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(3, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-3, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);

    // Check that the ACK gets ignored.
    SendInputEventACK(WebInputEvent::GestureScrollUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, last_acked_event().type());
    RunUntilIdle();
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    // The flag should have been flipped back to false.
    EXPECT_FALSE(WillIgnoreNextACK());

    // Enqueued.
    SimulateGestureScrollUpdateEvent(2, -2, 2);

    // Shouldn't coalesce with different modifiers.
    EXPECT_EQ(4U, GestureEventQueueSize());
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(2, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-2, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(2, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);
    merged_event = GestureEventSecondFromLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type());
    EXPECT_EQ(0.5, merged_event.data.pinchUpdate.scale);
    EXPECT_EQ(1, merged_event.modifiers());
    EXPECT_EQ(blink::WebGestureDeviceTouchscreen, merged_event.sourceDevice);

    // Check that the ACK sends the next scroll pinch pair.
    SendInputEventACK(WebInputEvent::GesturePinchUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type());
    RunUntilIdle();
    EXPECT_EQ(2U, GetAndResetSentGestureEventCount());

    // Check that the ACK sends the second message.
    SendInputEventACK(WebInputEvent::GestureScrollUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, last_acked_event().type());
    RunUntilIdle();
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());

    // Check that the ACK sends the second event.
    SendInputEventACK(WebInputEvent::GesturePinchUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type());
    RunUntilIdle();
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());

    // Check that the queue is empty after ACK and no events get sent.
    SendInputEventACK(WebInputEvent::GestureScrollUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, last_acked_event().type());
    RunUntilIdle();
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(0U, GestureEventQueueSize());
}

TEST_F(GestureEventQueueTest, CoalescesMultiplePinchEventSequences)
{
    // Simulate a pinch sequence.
    SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
        blink::WebGestureDeviceTouchscreen);
    SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
        blink::WebGestureDeviceTouchscreen);

    SimulateGestureScrollUpdateEvent(8, -4, 1);
    // Make sure that the queue contains what we think it should.
    WebGestureEvent merged_event = GestureEventLastQueueEvent();
    size_t expected_events_in_queue = 3;
    EXPECT_EQ(expected_events_in_queue, GestureEventQueueSize());
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());

    // Coalesced without changing event order. Note anchor at (60, 60). Anchoring
    // from a point that is not the origin should still give us the right scroll.
    SimulateGesturePinchUpdateEvent(1.5, 60, 60, 1);
    EXPECT_EQ(++expected_events_in_queue, GestureEventQueueSize());
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type());
    EXPECT_EQ(1.5, merged_event.data.pinchUpdate.scale);
    EXPECT_EQ(1, merged_event.modifiers());
    merged_event = GestureEventSecondFromLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(8, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-4, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(1, merged_event.modifiers());

    // Enqueued.
    SimulateGestureScrollUpdateEvent(6, -3, 1);

    // Check whether coalesced correctly.
    EXPECT_EQ(expected_events_in_queue, GestureEventQueueSize());
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type());
    EXPECT_EQ(1.5, merged_event.data.pinchUpdate.scale);
    EXPECT_EQ(1, merged_event.modifiers());
    merged_event = GestureEventSecondFromLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(12, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-6, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(1, merged_event.modifiers());

    // Now start another sequence before the previous sequence has been ack'ed.
    SimulateGestureEvent(WebInputEvent::GesturePinchEnd,
        blink::WebGestureDeviceTouchscreen);
    SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
        blink::WebGestureDeviceTouchscreen);
    SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
        blink::WebGestureDeviceTouchscreen);
    SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
        blink::WebGestureDeviceTouchscreen);

    SimulateGestureScrollUpdateEvent(8, -4, 1);
    // Make sure that the queue contains what we think it should.
    expected_events_in_queue += 5;
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(expected_events_in_queue, GestureEventQueueSize());
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());

    // Coalesced without changing event order. Note anchor at (60, 60). Anchoring
    // from a point that is not the origin should still give us the right scroll.
    SimulateGesturePinchUpdateEvent(1.5, 30, 30, 1);
    EXPECT_EQ(++expected_events_in_queue, GestureEventQueueSize());
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type());
    EXPECT_EQ(1.5, merged_event.data.pinchUpdate.scale);
    EXPECT_EQ(1, merged_event.modifiers());
    merged_event = GestureEventSecondFromLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(8, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-4, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(1, merged_event.modifiers());

    // Enqueued.
    SimulateGestureScrollUpdateEvent(6, -3, 1);

    // Check whether coalesced correctly.
    EXPECT_EQ(expected_events_in_queue, GestureEventQueueSize());
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, merged_event.type());
    EXPECT_EQ(1.5, merged_event.data.pinchUpdate.scale);
    EXPECT_EQ(1, merged_event.modifiers());
    merged_event = GestureEventSecondFromLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());
    EXPECT_EQ(12, merged_event.data.scrollUpdate.deltaX);
    EXPECT_EQ(-6, merged_event.data.scrollUpdate.deltaY);
    EXPECT_EQ(1, merged_event.modifiers());
}

TEST_F(GestureEventQueueTest, CoalescesPinchSequencesWithEarlyAck)
{
    SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
        blink::WebGestureDeviceTouchscreen);
    SendInputEventACK(WebInputEvent::GestureScrollBegin,
        INPUT_EVENT_ACK_STATE_CONSUMED);

    SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
        blink::WebGestureDeviceTouchscreen);
    SendInputEventACK(WebInputEvent::GesturePinchBegin,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    // ScrollBegin and PinchBegin have been sent
    EXPECT_EQ(2U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(0U, GestureEventQueueSize());

    SimulateGestureScrollUpdateEvent(5, 5, 1);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate,
        GestureEventLastQueueEvent().type());
    EXPECT_EQ(1U, GestureEventQueueSize());

    SimulateGesturePinchUpdateEvent(2, 60, 60, 1);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate,
        GestureEventLastQueueEvent().type());
    EXPECT_EQ(2U, GestureEventQueueSize());

    SimulateGesturePinchUpdateEvent(3, 60, 60, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate,
        GestureEventLastQueueEvent().type());
    EXPECT_EQ(3U, GestureEventQueueSize());

    SimulateGestureScrollUpdateEvent(5, 5, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    // The coalesced pinch/scroll pair will have been re-arranged, with the
    // pinch following the scroll.
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate,
        GestureEventLastQueueEvent().type());
    EXPECT_EQ(4U, GestureEventQueueSize());

    SimulateGesturePinchUpdateEvent(4, 60, 60, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(4U, GestureEventQueueSize());

    SendInputEventACK(WebInputEvent::GestureScrollUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(3U, GestureEventQueueSize());

    SendInputEventACK(WebInputEvent::GesturePinchUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(2U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());
    EXPECT_EQ(2.f, last_acked_event().data.pinchUpdate.scale);

    SendInputEventACK(WebInputEvent::GestureScrollUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, last_acked_event().type());

    SendInputEventACK(WebInputEvent::GesturePinchUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type());
    EXPECT_EQ(3.f * 4.f, last_acked_event().data.pinchUpdate.scale);

    EXPECT_EQ(0U, GestureEventQueueSize());
}

TEST_F(GestureEventQueueTest,
    DoesNotCoalescePinchGestureEventsWithDifferentModifiers)
{
    // Insert an event to force queueing of gestures.
    SimulateGestureEvent(WebInputEvent::GestureTapCancel,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(1U, GestureEventQueueSize());

    SimulateGestureScrollUpdateEvent(5, 5, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());

    SimulateGesturePinchUpdateEvent(3, 60, 60, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(3U, GestureEventQueueSize());

    SimulateGestureScrollUpdateEvent(10, 15, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(3U, GestureEventQueueSize());

    SimulateGesturePinchUpdateEvent(4, 60, 60, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(3U, GestureEventQueueSize());

    // Using different modifiers should prevent coalescing.
    SimulateGesturePinchUpdateEvent(5, 60, 60, 2);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(4U, GestureEventQueueSize());

    SimulateGesturePinchUpdateEvent(6, 60, 60, 3);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(5U, GestureEventQueueSize());

    SendInputEventACK(WebInputEvent::GestureTapCancel,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(2U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(4U, GestureEventQueueSize());

    SendInputEventACK(WebInputEvent::GestureScrollUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, last_acked_event().type());
    EXPECT_EQ(3U, GestureEventQueueSize());

    SendInputEventACK(WebInputEvent::GesturePinchUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type());
    EXPECT_EQ(3.f * 4.f, last_acked_event().data.pinchUpdate.scale);
    EXPECT_EQ(2U, GestureEventQueueSize());
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());

    SendInputEventACK(WebInputEvent::GesturePinchUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type());
    EXPECT_EQ(5.f, last_acked_event().data.pinchUpdate.scale);
    EXPECT_EQ(1U, GestureEventQueueSize());
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());

    SendInputEventACK(WebInputEvent::GesturePinchUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type());
    EXPECT_EQ(6.f, last_acked_event().data.pinchUpdate.scale);
    EXPECT_EQ(0U, GestureEventQueueSize());
}

TEST_F(GestureEventQueueTest, CoalescesScrollAndPinchEventsIdentity)
{
    // Insert an event to force queueing of gestures.
    SimulateGestureEvent(WebInputEvent::GestureTapCancel,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(1U, GestureEventQueueSize());

    // Ensure that coalescing yields an identity transform for any pinch/scroll
    // pair combined with its inverse.
    SimulateGestureScrollUpdateEvent(5, 5, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());

    SimulateGesturePinchUpdateEvent(5, 10, 10, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(3U, GestureEventQueueSize());

    SimulateGesturePinchUpdateEvent(.2f, 10, 10, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(3U, GestureEventQueueSize());

    SimulateGestureScrollUpdateEvent(-5, -5, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(3U, GestureEventQueueSize());

    SendInputEventACK(WebInputEvent::GestureTapCancel,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(2U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());

    SendInputEventACK(WebInputEvent::GestureScrollUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, last_acked_event().type());
    EXPECT_EQ(0.f, last_acked_event().data.scrollUpdate.deltaX);
    EXPECT_EQ(0.f, last_acked_event().data.scrollUpdate.deltaY);

    SendInputEventACK(WebInputEvent::GesturePinchUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type());
    EXPECT_EQ(1.f, last_acked_event().data.pinchUpdate.scale);
    EXPECT_EQ(0U, GestureEventQueueSize());

    // Insert an event to force queueing of gestures.
    SimulateGestureEvent(WebInputEvent::GestureTapCancel,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(1U, GestureEventQueueSize());

    // Ensure that coalescing yields an identity transform for any pinch/scroll
    // pair combined with its inverse.
    SimulateGesturePinchUpdateEvent(2, 10, 10, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());

    SimulateGestureScrollUpdateEvent(20, 20, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(3U, GestureEventQueueSize());

    SimulateGesturePinchUpdateEvent(0.5f, 20, 20, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(3U, GestureEventQueueSize());

    SimulateGestureScrollUpdateEvent(-5, -5, 1);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(3U, GestureEventQueueSize());

    SendInputEventACK(WebInputEvent::GestureTapCancel,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(2U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());

    SendInputEventACK(WebInputEvent::GestureScrollUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, last_acked_event().type());
    EXPECT_EQ(0.f, last_acked_event().data.scrollUpdate.deltaX);
    EXPECT_EQ(0.f, last_acked_event().data.scrollUpdate.deltaY);

    SendInputEventACK(WebInputEvent::GesturePinchUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type());
    EXPECT_EQ(1.f, last_acked_event().data.pinchUpdate.scale);
}

// Tests a single event with an synchronous ack.
TEST_F(GestureEventQueueTest, SimpleSyncAck)
{
    set_synchronous_ack(INPUT_EVENT_ACK_STATE_CONSUMED);
    SimulateGestureEvent(WebInputEvent::GestureTapDown,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(0U, GestureEventQueueSize());
    EXPECT_EQ(1U, GetAndResetAckedGestureEventCount());
}

// Tests an event with an synchronous ack which enqueues an additional event.
TEST_F(GestureEventQueueTest, SyncAckQueuesEvent)
{
    std::unique_ptr<WebGestureEvent> queued_event;
    set_synchronous_ack(INPUT_EVENT_ACK_STATE_CONSUMED);
    set_sync_followup_event(WebInputEvent::GestureShowPress,
        blink::WebGestureDeviceTouchscreen);
    // This event enqueues the show press event.
    SimulateGestureEvent(WebInputEvent::GestureTapDown,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(2U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(1U, GestureEventQueueSize());
    EXPECT_EQ(1U, GetAndResetAckedGestureEventCount());

    SendInputEventACK(WebInputEvent::GestureShowPress,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(0U, GestureEventQueueSize());
    EXPECT_EQ(1U, GetAndResetAckedGestureEventCount());
}

// Tests an event with an async ack followed by an event with a sync ack.
TEST_F(GestureEventQueueTest, AsyncThenSyncAck)
{
    SimulateGestureEvent(WebInputEvent::GestureTapDown,
        blink::WebGestureDeviceTouchscreen);

    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(1U, GestureEventQueueSize());
    EXPECT_EQ(0U, GetAndResetAckedGestureEventCount());

    SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
        blink::WebGestureDeviceTouchscreen);
    set_synchronous_ack(INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());
    EXPECT_EQ(0U, GetAndResetAckedGestureEventCount());

    SendInputEventACK(WebInputEvent::GestureTapDown,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(0U, GestureEventQueueSize());
    EXPECT_EQ(2U, GetAndResetAckedGestureEventCount());
}

TEST_F(GestureEventQueueTest, CoalescesScrollAndPinchEventWithSyncAck)
{
    // Simulate a pinch sequence.
    SimulateGestureEvent(WebInputEvent::GestureScrollBegin,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    SimulateGestureEvent(WebInputEvent::GesturePinchBegin,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());

    SimulateGestureScrollUpdateEvent(8, -4, 1);
    // Make sure that the queue contains what we think it should.
    WebGestureEvent merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(3U, GestureEventQueueSize());
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, merged_event.type());

    // Coalesced without changing event order. Note anchor at (60, 60). Anchoring
    // from a point that is not the origin should still give us the right scroll.
    SimulateGesturePinchUpdateEvent(1.5, 60, 60, 1);
    EXPECT_EQ(4U, GestureEventQueueSize());

    SendInputEventACK(WebInputEvent::GestureScrollBegin,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(3U, GestureEventQueueSize());

    // Ack the PinchBegin, and schedule a synchronous ack for GestureScrollUpdate.
    set_synchronous_ack(INPUT_EVENT_ACK_STATE_CONSUMED);
    SendInputEventACK(WebInputEvent::GesturePinchBegin,
        INPUT_EVENT_ACK_STATE_CONSUMED);

    // Both GestureScrollUpdate and GesturePinchUpdate should have been sent.
    EXPECT_EQ(WebInputEvent::GestureScrollUpdate, last_acked_event().type());
    EXPECT_EQ(1U, GestureEventQueueSize());
    EXPECT_EQ(2U, GetAndResetSentGestureEventCount());

    // Ack the final GesturePinchUpdate.
    SendInputEventACK(WebInputEvent::GesturePinchUpdate,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    EXPECT_EQ(WebInputEvent::GesturePinchUpdate, last_acked_event().type());
    EXPECT_EQ(0U, GestureEventQueueSize());
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
}

#if GTEST_HAS_PARAM_TEST
TEST_P(GestureEventQueueWithSourceTest, GestureFlingCancelsFiltered)
{
    WebGestureDevice source_device = GetParam();

    // GFC without previous GFS is dropped.
    SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(0U, GestureEventQueueSize());

    // GFC after previous GFS is dispatched and acked.
    SimulateGestureFlingStartEvent(0, -10, source_device);
    EXPECT_TRUE(FlingInProgress());
    SendInputEventACK(WebInputEvent::GestureFlingStart,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    RunUntilIdle();
    EXPECT_EQ(1U, GetAndResetAckedGestureEventCount());
    SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
    EXPECT_FALSE(FlingInProgress());
    EXPECT_EQ(2U, GetAndResetSentGestureEventCount());
    SendInputEventACK(WebInputEvent::GestureFlingCancel,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    RunUntilIdle();
    EXPECT_EQ(1U, GetAndResetAckedGestureEventCount());
    EXPECT_EQ(0U, GestureEventQueueSize());

    // GFC before previous GFS is acked.
    SimulateGestureFlingStartEvent(0, -10, source_device);
    EXPECT_TRUE(FlingInProgress());
    SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
    EXPECT_FALSE(FlingInProgress());
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());

    // Advance state realistically.
    SendInputEventACK(WebInputEvent::GestureFlingStart,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    RunUntilIdle();
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    SendInputEventACK(WebInputEvent::GestureFlingCancel,
        INPUT_EVENT_ACK_STATE_CONSUMED);
    RunUntilIdle();
    EXPECT_EQ(2U, GetAndResetAckedGestureEventCount());
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(0U, GestureEventQueueSize());

    // GFS is added to the queue if another event is pending
    SimulateGestureScrollUpdateEvent(8, -7, 0);
    SimulateGestureFlingStartEvent(0, -10, source_device);
    EXPECT_EQ(2U, GestureEventQueueSize());
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    WebGestureEvent merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureFlingStart, merged_event.type());
    EXPECT_TRUE(FlingInProgress());
    EXPECT_EQ(2U, GestureEventQueueSize());

    // GFS in queue means that a GFC is added to the queue
    SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureFlingCancel, merged_event.type());
    EXPECT_FALSE(FlingInProgress());
    EXPECT_EQ(3U, GestureEventQueueSize());

    // Adding a second GFC is dropped.
    SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
    EXPECT_FALSE(FlingInProgress());
    EXPECT_EQ(3U, GestureEventQueueSize());

    // Adding another GFS will add it to the queue.
    SimulateGestureFlingStartEvent(0, -10, source_device);
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureFlingStart, merged_event.type());
    EXPECT_TRUE(FlingInProgress());
    EXPECT_EQ(4U, GestureEventQueueSize());

    // GFS in queue means that a GFC is added to the queue
    SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureFlingCancel, merged_event.type());
    EXPECT_FALSE(FlingInProgress());
    EXPECT_EQ(5U, GestureEventQueueSize());

    // Adding another GFC with a GFC already there is dropped.
    SimulateGestureEvent(WebInputEvent::GestureFlingCancel, source_device);
    merged_event = GestureEventLastQueueEvent();
    EXPECT_EQ(WebInputEvent::GestureFlingCancel, merged_event.type());
    EXPECT_FALSE(FlingInProgress());
    EXPECT_EQ(5U, GestureEventQueueSize());
}

INSTANTIATE_TEST_CASE_P(AllSources,
    GestureEventQueueWithSourceTest,
    testing::Values(blink::WebGestureDeviceTouchscreen,
        blink::WebGestureDeviceTouchpad));
#endif // GTEST_HAS_PARAM_TEST

// Test that a GestureScrollEnd | GestureFlingStart are deferred during the
// debounce interval, that Scrolls are not and that the deferred events are
// sent after that timer fires.
TEST_F(GestureEventQueueTest, DebounceDefersFollowingGestureEvents)
{
    SetUpForDebounce(3);

    SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(1U, GestureEventQueueSize());
    EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
    EXPECT_TRUE(ScrollingInProgress());

    SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());
    EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
    EXPECT_TRUE(ScrollingInProgress());

    SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());
    EXPECT_EQ(1U, GestureEventDebouncingQueueSize());

    SimulateGestureFlingStartEvent(0, 10, blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());
    EXPECT_EQ(2U, GestureEventDebouncingQueueSize());

    SimulateGestureEvent(WebInputEvent::GestureTapDown,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());
    EXPECT_EQ(3U, GestureEventDebouncingQueueSize());

    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
        FROM_HERE, base::MessageLoop::QuitWhenIdleClosure(),
        TimeDelta::FromMilliseconds(5));
    base::RunLoop().Run();

    // The deferred events are correctly queued in coalescing queue.
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(5U, GestureEventQueueSize());
    EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
    EXPECT_FALSE(ScrollingInProgress());

    // Verify that the coalescing queue contains the correct events.
    WebInputEvent::Type expected[] = {
        WebInputEvent::GestureScrollUpdate,
        WebInputEvent::GestureScrollUpdate,
        WebInputEvent::GestureScrollEnd,
        WebInputEvent::GestureFlingStart
    };

    for (unsigned i = 0; i < sizeof(expected) / sizeof(WebInputEvent::Type);
         i++) {
        WebGestureEvent merged_event = GestureEventQueueEventAt(i);
        EXPECT_EQ(expected[i], merged_event.type());
    }
}

// Test that non-scroll events are deferred while scrolling during the debounce
// interval and are discarded if a GestureScrollUpdate event arrives before the
// interval end.
TEST_F(GestureEventQueueTest, DebounceDropsDeferredEvents)
{
    SetUpForDebounce(3);

    EXPECT_FALSE(ScrollingInProgress());

    SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(1U, GestureEventQueueSize());
    EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
    EXPECT_TRUE(ScrollingInProgress());

    // This event should get discarded.
    SimulateGestureEvent(WebInputEvent::GestureScrollEnd,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(1U, GestureEventQueueSize());
    EXPECT_EQ(1U, GestureEventDebouncingQueueSize());

    SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
        blink::WebGestureDeviceTouchscreen);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());
    EXPECT_EQ(0U, GestureEventDebouncingQueueSize());
    EXPECT_TRUE(ScrollingInProgress());

    // Verify that the coalescing queue contains the correct events.
    WebInputEvent::Type expected[] = {
        WebInputEvent::GestureScrollUpdate,
        WebInputEvent::GestureScrollUpdate
    };

    for (unsigned i = 0; i < sizeof(expected) / sizeof(WebInputEvent::Type);
         i++) {
        WebGestureEvent merged_event = GestureEventQueueEventAt(i);
        EXPECT_EQ(expected[i], merged_event.type());
    }
}

TEST_F(GestureEventQueueTest, CoalescesSyntheticScrollBeginEndEvents)
{
    // Test coalescing of only GestureScrollBegin/End events.
    SimulateGestureEvent(WebInputEvent::GestureScrollUpdate,
        blink::WebGestureDeviceTouchpad);
    EXPECT_EQ(1U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(1U, GestureEventQueueSize());

    WebGestureEvent synthetic_end = SyntheticWebGestureEventBuilder::Build(
        WebInputEvent::GestureScrollEnd, blink::WebGestureDeviceTouchpad);
    synthetic_end.data.scrollEnd.synthetic = true;

    SimulateGestureEvent(synthetic_end);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(2U, GestureEventQueueSize());

    // Synthetic begin will remove the unsent synthetic end.
    WebGestureEvent synthetic_begin = SyntheticWebGestureEventBuilder::Build(
        WebInputEvent::GestureScrollBegin, blink::WebGestureDeviceTouchpad);
    synthetic_begin.data.scrollBegin.synthetic = true;

    SimulateGestureEvent(synthetic_begin);
    EXPECT_EQ(0U, GetAndResetSentGestureEventCount());
    EXPECT_EQ(1U, GestureEventQueueSize());
}

} // namespace content
